#   Copyright (C) 2008      Tri Tech Information Systems Inc.
# 
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2 of the License, or
#   (at your option) any later version.
# 
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
# 
#   You should have received a copy of the GNU General Public License along
#   with this program; if not, write to the Free Software Foundation, Inc.,
#   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#   

from formatters import GeneratorAdaptor, generator, indent
from parser import CodeModelItem
import sys

def fully_scoped_name(item):
    return '::' + '::'.join(item.qualifiedName)

class Wrapper( object ):
    '''Base class for all wrapper generator objects
    '''
#    name = property( lambda s: s.item.name )
    qualifiedName = property( lambda s: fully_scoped_name(s.item) )
    quotedName = property( lambda s: '"'+s.name+'"' )

    def __init__( self, item, parent = None, python_name = None ):
        self.parent = parent
        self.item = item
        try:
            self.wrapper_ctx
        except AttributeError:
            self.wrapper_ctx = parent.wrapper_ctx

        if python_name is None and item is not None:
            if item.name == 'operator()':
                python_name = "__call__"
            else:
                python_name = item.name
        
        self.python_name = python_name
        if self.python_name:
            self.quoted_python_name = '"' + self.python_name + '"'            
        else:
            self.quoted_python_name = None

        self.prepared = False            
        if self.wrapper_ctx is not self:
            self.docstring = self.wrapper_ctx.docstring_source.request(self.item)
        else:
            self.docstring = None


    def __getattr__( self, attr ):
        try:
            return getattr( self.item, attr )
        except AttributeError:
            raise AttributeError("%s not found on %s" % (attr, repr(self)))

    @generator
    def generate( self, templ = None ):
        if templ is None:
            templ = self.TEMPLATE
        try:            
            return templ % GeneratorAdaptor( self )
        except:
            print >> sys.stderr, templ
            raise

    def resolveType( self, typenfo ):
        scope = self.item.scope
        model = self.item.model
        ns = model.globalNamespace

        while scope:
            item = model.findItem( scope, ns )
            typenfo = typenfo.resolveType( item )

            # Walk up the scope list
            scope = item.scope

        return typenfo

    def prepare(self):
        if not self.prepared:
            self.prepared = True
            self.do_prepare()


def _scoped(text, scopes):
    """
    Returns the text placed into the scopes
    """
    if not scopes:
        return text
    else:
        head = scopes[0]
        tail = scopes[1:]

        subtext = _scoped(text, tail)

        return "%s {\n%s\n}\n" % (head, indent(subtext) )

def _scope_of(item):
    """
    Return the scope of item
    """
    if not item.scope:
        return []
    else:
        model = item.model
        ns = model.globalNamespace
        
        return model.findItem( item.scope, ns )


class ExposedWrapper( Wrapper ):
    '''Base class for wrapper generators that inserts wrappers into a special namespace
    '''
    TEMPLATE = '''
    %(header_extra)s

    struct Wrap_%(name)s %(wrapper_bases)s
    {
        static bool __Ready()
        {
            %(scopetest)s
            %(readylines)s

            return true;
        }
        
        static void __WrapClass()
        {
            %(setup_scope)s

            %(wrapper_code)s

            %(extras)s                
        }

        %(class_wrapper_code)s

        %(classextra)s

        static RegisterWrapper<Wrap_%(name)s> register_class;
    };        
    RegisterWrapper<Wrap_%(name)s> Wrap_%(name)s::register_class("%(module)s");


    '''
    CLASS_TEMPLATE = ""

    def __init__(self, wrapper_ctx, *args, **kargs):
        self.wrapper_ctx = wrapper_ctx
        Wrapper.__init__(self, *args, **kargs)
        self.cpp_scope = None
        self.header_extra = ""
        self.classextra = ""
        self.extras = []

    def scopes(self):
        scopes = []
        scope = self
        scopes.append( "namespace __python_wrapper" )
        while scope:
            if scope.kind == CodeModelItem.Kind.Namespace:
                scopes.append( ("namespace %s " % scope.name) )                    
            else:
                scopes.append( ("namespace class_%s" % scope.name) )
            scope = _scope_of(scope)

        scopes.reverse()
        return scopes

    def generate_readylines(self): return ""

    def generate(self, tmpl = None):
        text = Wrapper.generate(self, tmpl)
        if tmpl is self.TEMPLATE or tmpl is None:
            return _scoped(text, self.scopes())
        else:
            return text

    def addExtra(self, txt):
        self.extras.append(txt)

    def generate_wrapper_code(self):
        return self.generate(self.WRAPPER_TEMPLATE)

    def generate_wrapper_bases(self):
        return ""

    def generate_class_wrapper_code(self):
        return self.generate(self.CLASS_TEMPLATE)

    def SetScope(self, cpp_scope):
        self.cpp_scope = cpp_scope

    @generator
    def generate_setup_scope(self):        
        if self.cpp_scope:
            if self.cpp_scope in self.wrapper_ctx.namespaces:
                ns = self.wrapper_ctx.namespaces[self.cpp_scope]
                return """
                scope this_scope = bpy::object(import("pyxx_helper").attr("__namespaces__")["%s"]);
                """ % ns.ns_name
            else:

                return """
                using namespace boost::python;
                using namespace boost::python::converter;
                registration const * p = registry::query( type_id<%(cpp_scope)s>() );
                if(!p)
                    throw std::logic_error( std::string(type_id<%(cpp_scope)s>().name()) + " has not been wrapped");
                scope this_scope = object(handle<>(borrowed(p->get_class_object())));
                """
        else:
            return "scope this_scope;"


    @generator
    def generate_scopetest(self):      
        """
        This code generates a scope test, which tests to see whether a scope exists or not
        It must exist prior to the export going through
        """

        if self.cpp_scope:
            # There are two types of scopes, namespace and classes
            if self.cpp_scope in self.wrapper_ctx.namespaces:
                ns = self.wrapper_ctx.namespaces[self.cpp_scope]
                return """
                bpy::object nss = import("pyxx_helper").attr("__namespaces__");
                try
                {
                    nss["%s"];
                }
                catch(boost::python::error_already_set&)
                {
                    return false;
                }

                """ % (ns.ns_name)
            else:
                return "if(!is_wrapped<%(cpp_scope)s>()) return false;"
        else:
            return ""
